package processevent

import (
	"os"
	"strings"
	"io/ioutil"
	"bufio"
	"cloud_worm/worm/client/monitor/monitorlog"
	"cloud_worm/worm/client/docker"
	"errors"
	"time"
)

var (
	NoPushProcessError = errors.New("no need push process event")
)

type Process struct {
	Type 			int    	`json:"type"` // 标示type
	Ip   			string 	`json:"ip"`
	DockerId 		string	`json:"docker"`
	Pid  			string 	`json:"pid"`

	CwdPath   string `json:"cwd_path"`
	ExePath   string `json:"exe_path"`
	Cmdline   string `json:"cmdline"`
	Uid       string `json:"uid"`
	Gid       string `json:"gid"`
	StartTime string `json:"start_time"`

	Ppid     string `json:"ppid"`
	PCwdPath string `json:"p_cwd_path"`
	PExePath string `json:"p_exe_path"`
	PCmdline string `json:"p_cmdline"`
	EvilCmd  int    `json:"evil_cmd"` //标示是不是恶意命令，现在没用了，先留在
}

func (p *Process) Analyzer(selfPid string, pid string, ip string) (bool, error) {
	p.Type = monitorlog.CmdLogType
	p.Pid = pid

	p.EvilCmd = 0

	// parent进程处理
	record, err := p.aboutParent(selfPid, pid)
	if err != nil {
		return false, err			// 解析父进程出错，不记录
	}
	if record == false {
		return false, nil			// 解析父进程正确，但是匹配不记录
	}

	// exe path
	name, err := os.Readlink("/proc/" + pid + "/exe")
	if err != nil {
		return false, err
	}
	p.ExePath = name

	// cmdline
	data, err := ioutil.ReadFile("/proc/" + pid + "/cmdline")
	if err != nil {
		return false, err
	}
	p.Cmdline = strings.TrimSpace(strings.Replace(string(data), "\u0000", " ", -1))
	if len(p.Cmdline) >= 1024 {
		return false, NoPushProcessError
	}
	// self本身的匹配
	if p.selfWhiteMatch(p.Cmdline + p.ExePath + p.CwdPath) {
		return false, nil				// 匹配关键字，不记录
	}

	// get docker ip
	if docker.ContainInfoInst.SystemType == docker.DockerHost {
		// 如果是宿主机，则查docker ip
		content, err := ioutil.ReadFile("/proc/" + pid + "/cgroup")
		if err == nil {
			var containID string
			for _, l := range strings.Split(string(content), "\n") {
				index := strings.LastIndex(l, "/docker/")
				if index == -1 {
					continue
				}

				containID = l[index + len("/docker/"):]
				break
			}
			if len(containID) > 12 {
				p.DockerId = containID[:12]
				if containIP := docker.ContainInfoInst.GetIPById(containID); containIP != "" {
					ip = containIP
				}
			}
		}
	}
	p.Ip = ip
	// start time，改为直接取当前时间即可
	//infos, err := os.Stat("/proc/" + pid + "/cmdline")
	//if err != nil {
	//	return false, err
	//}
	//p.StartTime = infos.ModTime().Format("2006-01-02 15:04:05")
	p.StartTime = time.Now().Format("2006-01-02 15:04:05")

	return true, nil
}

func (p *Process) aboutParent(selfPid string, pid string) (bool, error) {
	// Uid/Gid/Ppid
	fp, err := os.Open("/proc/" + pid + "/status")
	if err != nil {
		return false, err
	}
	defer fp.Close()

	scanner := bufio.NewScanner(fp)
	for scanner.Scan() {
		line := scanner.Text()
		if len(line) <= 0 {
			continue
		}

		if strings.HasPrefix(line, "PPid") {
			p.Ppid = strings.TrimSpace(line[strings.Index(line, ":")+1:])
			if p.Ppid == selfPid {
				// 父进程是wolagent，则不记录
				return false, nil
			}

			// 父进程的信息 获取一下 cwd_path  exe_path cmdline
			// exe path
			name, err := os.Readlink("/proc/" + p.Ppid + "/exe")
			if err != nil {
				continue
			}
			p.PExePath = name

			// cwd
			currPath, err := os.Readlink("/proc/" + p.Ppid + "/cwd")
			if err != nil {
				continue
			}
			p.PCwdPath = currPath

			// cmdline
			data, err := ioutil.ReadFile("/proc/" + p.Ppid + "/cmdline")
			if err != nil {
				continue
			}
			p.PCmdline = strings.TrimSpace(strings.Replace(string(data), "\u0000", " ", -1))

		} else if strings.HasPrefix(line, "Uid") {
			p.Uid = strings.TrimSpace(strings.Split(line[strings.Index(line, ":")+1:], "\t")[1])
		} else if strings.HasPrefix(line, "Gid") {
			p.Gid = strings.TrimSpace(strings.Split(line[strings.Index(line, ":")+1:], "\t")[1])
		}
	}

	// 父进程过滤
	if p.parentWhiteMatch(p.PCmdline + p.PExePath + p.PCwdPath) {
		return false, nil
	}

	return true, nil
}

func (p *Process) parentWhiteMatch(parentCmd string) bool {

	WhiteListInstance.ParentList.Lock()
	if len(WhiteListInstance.ParentList.List) == 0 {
		for _, item := range defaultParentList {
			if strings.Contains(parentCmd, item) {
				WhiteListInstance.ParentList.Unlock()
				return true
			}
		}
	} else {
		for _, item := range WhiteListInstance.ParentList.List {
			if strings.Contains(parentCmd, item) {
				WhiteListInstance.ParentList.Unlock()
				return true
			}
		}
	}

	WhiteListInstance.ParentList.Unlock()
	return false
}

func (p *Process) selfWhiteMatch(selfCmd string) bool {
	WhiteListInstance.SelfList.Lock()
	if len(WhiteListInstance.SelfList.List) == 0 {
		for _, item := range defaultSelfList {
			if strings.Contains(selfCmd, item) {
				WhiteListInstance.SelfList.Unlock()
				return true
			}
		}
	} else {
		for _, item := range WhiteListInstance.SelfList.List {
			if strings.Contains(selfCmd, item) {
				WhiteListInstance.SelfList.Unlock()
				return true
			}
		}
	}

	WhiteListInstance.SelfList.Unlock()
	return false
}
